package com.office.common.utils.minio;

import io.minio.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

/**
 * MinIO 工具类封装
 * <p>
 * 提供文件上传、下载、删除、预览等功能。
 * 所有操作基于 MinIO Java SDK 实现。
 *
 * @author: 柒月
 * @date: 2025/5/28 下午1:28
 * @version: 1.0
 */
@Component
public class MinioUtils {

    /**
     * 注入配置类：从 application.yml 中读取 MinIO 配置参数
     */
    @Autowired
    private MinioProperties minioProperties;

    /**
     * MinIO 客户端实例，用于执行各种对象存储操作
     */
    private final MinioClient minioClient;

    /**
     * 构造方法中初始化 MinIO 客户端连接
     *
     * @param minioProperties MinIO 配置信息（endpoint, accessKey, secretKey）
     */
    public MinioUtils(MinioProperties minioProperties) {
        this.minioClient = MinioClient.builder()
                .endpoint(minioProperties.getEndpoint()) // 设置 MinIO 服务地址
                .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()) // 设置访问密钥
                .build(); // 构建客户端实例
    }

    /**
     * 如果桶不存在，则创建桶
     *
     * @throws Exception 创建失败时抛出异常
     */
    public void createBucketIfNotExists() throws Exception {
        boolean found = minioClient.bucketExists(BucketExistsArgs.builder()
                .bucket(minioProperties.getBucketName()).build());
        if (!found) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(minioProperties.getBucketName()).build());
        }
    }

    /**
     * 获取文件扩展名
     *
     * @param originalFilename 原始文件名
     * @return 文件扩展名（包括点号 .）
     */
    private String getFileExtension(String originalFilename) {
        int dotIndex = originalFilename.lastIndexOf('.');
        return (dotIndex == -1) ? "" : originalFilename.substring(dotIndex);
    }

    /**
     * 自动重命名文件：使用 UUID + 扩展名，避免重复文件名冲突
     *
     * @param originalFilename 原始文件名
     * @return 新的唯一文件名
     */
    public String renameFile(String originalFilename) {
        String ext = getFileExtension(originalFilename);
        return UUID.randomUUID() + ext;
    }

    /**
     * 将文件上传到 MinIO 并返回存储名称
     *
     * @param originalFilename 原始文件名
     * @param inputStream      文件输入流
     * @param size             文件大小
     * @param contentType      文件 MIME 类型（如 image/jpeg）
     * @return 存储在 MinIO 中的文件名（UUID 格式）
     * @throws Exception 上传失败时抛出异常
     */
    public String upload(String originalFilename, InputStream inputStream, long size, String contentType) throws Exception {
        String objectName = renameFile(originalFilename); // 生成唯一文件名
        createBucketIfNotExists(); // 确保存储桶存在

        // 上传文件到 MinIO
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName)
                .stream(inputStream, size, -1)
                .contentType(contentType)
                .build());

        return objectName; // 返回存储名
    }

    /**
     * 上传文件并返回包含原始名、存储名、访问链接的结果对象
     *
     * @param originalFilename 原始文件名
     * @param inputStream      文件输入流
     * @param size             文件大小
     * @param contentType      文件 MIME 类型
     * @return 包含上传结果的对象
     * @throws Exception 上传失败时抛出异常
     */
    public FileUploadResult uploadWithResult(String originalFilename, InputStream inputStream, long size, String contentType) throws Exception {
        String storedName = upload(originalFilename, inputStream, size, contentType); // 上传并获取存储名
        String url = getPreviewUrl(storedName); // 生成预览链接
        return new FileUploadResult(originalFilename, storedName, url); // 返回结果对象
    }

    /**
     * 下载文件接口
     *
     * @param objectName 存储在 MinIO 中的文件名（UUID 格式）
     * @param response   HttpServletResponse 用于向浏览器输出文件流
     * @throws Exception 下载失败时抛出异常
     */
    public void download(String objectName, HttpServletResponse response) throws Exception {
        // 获取 MinIO 中的文件流
        InputStream obj = minioClient.getObject(GetObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName).build());

// 设置响应头，告知浏览器这是一个附件下载
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + objectName);

        byte[] buffer = new byte[1024];
        int len;
        while ((len = obj.read(buffer)) > 0) {
            response.getOutputStream().write(buffer, 0, len); // 写入输出流
        }
        response.getOutputStream().flush(); // 刷新输出流

        obj.close();
    }

    /**
     * 删除指定文件
     *
     * @param objectName 存储在 MinIO 中的文件名（UUID 格式）
     * @throws Exception 删除失败时抛出异常
     */
    public void delete(String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(minioProperties.getBucketName())
                .object(objectName).build());
    }

    /**
     * 获取文件预览链接
     *
     * @param objectName 存储在 MinIO 中的文件名（UUID 格式）
     * @return 完整的访问 URL 地址
     * @throws Exception 获取失败时抛出异常
     */
    public String getPreviewUrl(String objectName) throws Exception {
        return minioProperties.getEndpoint() + "/" + minioProperties.getBucketName() + "/" + objectName;
    }

    /**
     * 文件上传结果封装类
     */
    public static class FileUploadResult {
        private Long id; // 文件的id
        private String originalName;  // 原始文件名
        private String storedName;    // 存储在 MinIO 中的文件名
        private String url;           // 访问链接

        public FileUploadResult(String originalName, String storedName, String url) {
            this.originalName = originalName;
            this.storedName = storedName;
            this.url = url;
        }

        // Getter 和 Setter 方法（便于 JSON 序列化）


        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getOriginalName() {
            return originalName;
        }

        public void setOriginalName(String originalName) {
            this.originalName = originalName;
        }

        public String getStoredName() {
            return storedName;
        }

        public void setStoredName(String storedName) {
            this.storedName = storedName;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }

}